Private z_IDEflag As Long 'Flag indicating we are in IDE
Private z_ScMem As Long 'Thunk base address
Private z_scFunk As Collection 'hWnd/thunk-address collection
Private z_hkFunk As Collection 'hook/thunk-address collection
Private z_cbFunk As Collection 'callback/thunk-address collection
Private Const IDX_INDEX As Long = 2 'index of the subclassed hWnd OR hook type
Private Const IDX_CALLBACKORDINAL As Long = 22 ' Ubound(callback thunkdata)+1, index of the callback
' Declarations:
Private Declare Sub RtlMoveMemory Lib "kernel32" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
Private Declare Function IsBadCodePtr Lib "kernel32" (ByVal lpfn As Long) As Long
Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function GetModuleHandleA Lib "kernel32" (ByVal lpModuleName As String) As Long
Private Declare Function GetModuleHandleW Lib "kernel32" (ByVal lpModuleName As Long) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Enum eThunkType
SubclassThunk = 0
HookThunk = 1
CallbackThunk = 2
End Enum
'-Selfsub specific declarations----------------------------------------------------------------------------
Private Enum eMsgWhen 'When to callback
MSG_BEFORE = 1 'Callback before the original WndProc
MSG_AFTER = 2 'Callback after the original WndProc
MSG_BEFORE_AFTER = MSG_BEFORE Or MSG_AFTER 'Callback before and after the original WndProc
End Enum
Private Const IDX_WNDPROC As Long = 9 'Thunk data index of the original WndProc
Private Const IDX_BTABLE As Long = 11 'Thunk data index of the Before table
Private Const IDX_ATABLE As Long = 12 'Thunk data index of the After table
Private Const IDX_PARM_USER As Long = 13 'Thunk data index of the User-defined callback parameter data index
Private Const IDX_UNICODE As Long = 75 'Must be Ubound(subclass thunkdata)+1; index for unicode support
Private Const ALL_MESSAGES As Long = -1 'All messages callback
Private Const MSG_ENTRIES As Long = 32 'Number of msg table entries. Set to 1 if using ALL_MESSAGES for all subclassed windows
' \\LaVolpe - Added non-ANSI version API calls
Private Declare Function CallWindowProcA Lib "user32" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function CallWindowProcW Lib "user32" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function GetCurrentProcessId Lib "kernel32" () As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function IsWindowUnicode Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function SendMessageA Lib "user32.dll" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function SendMessageW Lib "user32.dll" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
Private Declare Function SetWindowLongA Lib "user32" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetWindowLongW Lib "user32" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
'* nOrdinal - Optional, ordinal index of the callback procedure. 1 = last private method, 2 = second last private method, etc.
'* oCallback - Optional, the object that will receive the callback. If undefined, callbacks are sent to this object's instance
'* bIdeSafety - Optional, enable/disable IDE safety measures. NB: you should really only disable IDE safety in a UserControl for design-time subclassing
'* bUnicode - Optional, if True, Unicode API calls will be made to the window vs ANSI calls
zError SUB_NAME, "Window handle is already subclassed"
ReleaseMemory:
VirtualFree z_ScMem, 0, MEM_RELEASE 'ssc_Subclass has failed after memory allocation, so release the memory
End Function
'Terminate all subclassing
Private Sub ssc_Terminate()
' can be made public. Releases all subclassing
' can be removed and zTerminateThunks can be called directly
zTerminateThunks SubclassThunk
End Sub
'UnSubclass the specified window handle
Private Sub ssc_UnSubclass(ByVal lng_hWnd As Long)
' can be made public. Releases a specific subclass
' can be removed and zUnThunk can be called directly
zUnThunk lng_hWnd, SubclassThunk
End Sub
'Add the message value to the window handle's specified callback table
Private Sub ssc_AddMsg(ByVal lng_hWnd As Long, ByVal uMsg As Long, Optional ByVal When As eMsgWhen = MSG_AFTER)
' Note: can be removed if not needed and zAddMsg can be called directly
If IsBadCodePtr(zMap_VFunction(lng_hWnd, SubclassThunk)) = 0 Then 'Ensure that the thunk hasn't already released its memory
If When And MSG_BEFORE Then 'If the message is to be added to the before original WndProc table...
zAddMsg uMsg, IDX_BTABLE 'Add the message to the before table
End If
If When And MSG_AFTER Then 'If message is to be added to the after original WndProc table...
zAddMsg uMsg, IDX_ATABLE 'Add the message to the after table
End If
End If
End Sub
'Delete the message value from the window handle's specified callback table
Private Sub ssc_DelMsg(ByVal lng_hWnd As Long, ByVal uMsg As Long, Optional ByVal When As eMsgWhen = MSG_AFTER)
' Note: can be removed if not needed and zDelMsg can be called directly
If IsBadCodePtr(zMap_VFunction(lng_hWnd, SubclassThunk)) = 0 Then 'Ensure that the thunk hasn't already released its memory
If When And MSG_BEFORE Then 'If the message is to be deleted from the before original WndProc table...
zDelMsg uMsg, IDX_BTABLE 'Delete the message from the before table
End If
If When And MSG_AFTER Then 'If the message is to be deleted from the after original WndProc table...
zDelMsg uMsg, IDX_ATABLE 'Delete the message from the after table
End If
End If
End Sub
'Call the original WndProc
Private Function ssc_CallOrigWndProc(ByVal lng_hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
' Note: can be removed if you do not use this function inside of your window procedure
If IsBadCodePtr(zMap_VFunction(lng_hWnd, SubclassThunk)) = 0 Then 'Ensure that the thunk hasn't already released its memory
If zData(IDX_UNICODE) Then
ssc_CallOrigWndProc = CallWindowProcW(zData(IDX_WNDPROC), lng_hWnd, uMsg, wParam, lParam) 'Call the original WndProc of the passed window handle parameter
Else
ssc_CallOrigWndProc = CallWindowProcA(zData(IDX_WNDPROC), lng_hWnd, uMsg, wParam, lParam) 'Call the original WndProc of the passed window handle parameter
End If
End If
End Function
'Get the subclasser lParamUser callback parameter
Private Function zGet_lParamUser(ByVal hWnd_Hook_ID As Long, vType As eThunkType) As Long
'Note: can be removed if you never need to retrieve/update your user-defined paramter. See ssc_Subclass
If vType <> CallbackThunk Then
If IsBadCodePtr(zMap_VFunction(hWnd_Hook_ID, vType)) = 0 Then 'Ensure that the thunk hasn't already released its memory
zGet_lParamUser = zData(IDX_PARM_USER) 'Get the lParamUser callback parameter
End If
End If
End Function
'Let the subclasser lParamUser callback parameter
Private Sub zSet_lParamUser(ByVal hWnd_Hook_ID As Long, vType As eThunkType, newValue As Long)
'Note: can be removed if you never need to retrieve/update your user-defined paramter. See ssc_Subclass
If vType <> CallbackThunk Then
If IsBadCodePtr(zMap_VFunction(hWnd_Hook_ID, vType)) = 0 Then 'Ensure that the thunk hasn't already released its memory
zData(IDX_PARM_USER) = newValue 'Set the lParamUser callback parameter
End If
End If
End Sub
'Add the message to the specified table of the window handle
Private Sub zAddMsg(ByVal uMsg As Long, ByVal nTable As Long)
Dim nCount As Long 'Table entry count
Dim nBase As Long 'Remember z_ScMem
Dim i As Long 'Loop index
nBase = z_ScMem 'Remember z_ScMem so that we can restore its value on exit
z_ScMem = zData(nTable) 'Map zData() to the specified table
If uMsg = ALL_MESSAGES Then 'If ALL_MESSAGES are being added to the table...
nCount = ALL_MESSAGES 'Set the table entry count to ALL_MESSAGES
Else
nCount = zData(0) 'Get the current table entry count
If nCount >= MSG_ENTRIES Then 'Check for message table overflow
zError "zAddMsg", "Message table overflow. Either increase the value of Const MSG_ENTRIES or use ALL_MESSAGES instead of specific message values"
GoTo Bail
End If
For i = 1 To nCount 'Loop through the table entries
If zData(i) = 0 Then 'If the element is free...
zData(i) = uMsg 'Use this element
GoTo Bail 'Bail
ElseIf zData(i) = uMsg Then 'If the message is already in the table...
GoTo Bail 'Bail
End If
Next i 'Next message table entry
nCount = i 'On drop through: i = nCount + 1, the new table entry count
zData(nCount) = uMsg 'Store the message in the appended table entry
End If
zData(0) = nCount 'Store the new table entry count
Bail:
z_ScMem = nBase 'Restore the value of z_ScMem
End Sub
'Delete the message from the specified table of the window handle
Private Sub zDelMsg(ByVal uMsg As Long, ByVal nTable As Long)
Dim nCount As Long 'Table entry count
Dim nBase As Long 'Remember z_ScMem
Dim i As Long 'Loop index
nBase = z_ScMem 'Remember z_ScMem so that we can restore its value on exit
z_ScMem = zData(nTable) 'Map zData() to the specified table
If uMsg = ALL_MESSAGES Then 'If ALL_MESSAGES are being deleted from the table...
zData(0) = 0 'Zero the table entry count
Else
nCount = zData(0) 'Get the table entry count
For i = 1 To nCount 'Loop through the table entries
If zData(i) = uMsg Then 'If the message is found...
zData(i) = 0 'Null the msg value -- also frees the element for re-use
GoTo Bail 'Bail
End If
Next i 'Next message table entry
zError "zDelMsg", "Message &H" & Hex$(uMsg) & " not found in table"
'Map zData() to the thunk address for the specified window handle
Private Function zMap_VFunction(ByVal vFuncTarget As Long, vType As eThunkType) As Long
' vFuncTarget is one of the following, depending on vType
' - Subclassing: the hWnd of the window subclassed
' - Hooking: the hook type created
' - Callbacks: the ordinal of the callback
Dim thunkCol As Collection
If vType = CallbackThunk Then
Set thunkCol = z_cbFunk
ElseIf vType = HookThunk Then
Set thunkCol = z_hkFunk
ElseIf vType = SubclassThunk Then
Set thunkCol = z_scFunk
Else
zError "zMap_Vfunction", "Invalid thunk type passed"
Exit Function
End If
If thunkCol Is Nothing Then
zError "zMap_VFunction", "Thunk hasn't been initialized"
Else
On Error GoTo Catch
z_ScMem = thunkCol("h" & vFuncTarget) 'Get the thunk address
zMap_VFunction = z_ScMem
End If
Exit Function 'Exit returning the thunk address
Catch:
zError "zMap_VFunction", "Thunk type for ID of " & vFuncTarget & " does not exist"
End Function
'Error handler
Private Sub zError(ByVal sRoutine As String, ByVal sMsg As String)
' \\LaVolpe - Note. These two lines can be rem'd out if you so desire. But don't remove the routine
App.LogEvent TypeName(Me) & "." & sRoutine & "r each nk type for ID of " & vFuncTarget & " pe fore)pate& ssssssssssLogEvDH(0 0 'Set th"p.Logicothe WndProc executiCpu(ta +uncT6uSet th"p.Logicothe WndProc executiCpu(ta +unou so dehe valuei hunk ck ordinal nk type fo the SetWindowsLong fe fo the SetWindowsLong fe f1ring = "scb_SeF the thunk wsLon22222222E,1 Private Su222222E,122222222E,22222E,1ll_Noti
2E,22222E,1llllllhnkCol = z_e array2 nst INDX_PARAMLEN As Longap_VFunction = z_ScMem
. But :b_SetCallIDX_WND71 pe fore)pate& sf vType =le
Pr7ogEven 'Bytemon =
' 'Exit ssc_Subcl be MIDX_W 'Sdeter "bUniIallIDX_WND71 pe fore)pb
'Map zData() to the thunk address for the specified window handle
b))))))unk return
c(0, code-------------------zDataE IDX_Wur 5the r============s mIallIDX_WNi'BT is already subc7he r=== alIallIxW0ave the routine
Ap ,,,en
'-RuP"IsBadC nk (9) = &Ht to thEnce iM=====siub GetTrayVersion()
' unmodified from sourc()
5the1
nBase = z_ScMem zDataE sourc()
&********(0, codeEEEEEEEEE)))))unac a2 1) As Byte
Irtualdre '===============c ,,,back(ByVal nOrdinal As Long)
able o==========SodeEEEEEe+ 16 'we Ac(IDIs NotUMciT ***(======SodciT *l nOrdiHt to thEncF-zDataElt to thEncF-zDataElt to thEhEncF-zDatH2975Ahat wiiHt to thEncF-h, dddddddddOrdiHHHHHHHHHH)n,ChEn'al 1, the se*t wiiHt to thEncF-h, d)'* nPar-------------)) = ===========AhEnconst IND o==AhEnconst INd last is ordinal 2, etc...
'* oCallback - Optional, the object that will receive the callback. If undefined, callbackthe kthe kthe kthe kthe kthe kthe kthe kthe kthe kt sou rn thef u==c indexrn a Long even if, per MSDN, the callback procedueataE the ==c 1f nCallback = 0 Thef this is thd directlyCt is one of the folloRlbackAddrCb(1O'* nPar-------------) 1f nCallback = 0 .453lback = 0 ThellIDXMA9 H4&:rhe kthe kthepCDN, (z_ScMem
aO8 a Long even if, per MSDN, the callback procedure is a Sub u the se*t wiiHt z
'Me = z_ScMe callbllIDXMA9 trPtr(z_hhhhhhU lback = 0, lbllIDMe car aO8 a Loback protring, h4&:rhe kthe kong even if, per MSDN
---feCONDATA_V1_SIZE
1)
iyVat l elemeIDXMAGES
le
suHHHH)n, u==c indexrn a Long ) = &H53FF2073: z_Sc(74) = &HC328Uonst INstore itz_ScMem
aO8 a Long even 1e itz_1 22ese two ress in th p********"p.k addah :rhe kthe kong evennarcMemt thBmt thBmt theCONDATA_V1_L i"dow subc****************nk data
RtlMov Const IDUI If nParamCou_V1_L i"dddddddRv Ac(I even taEltse*t wiiHt z
' a Long ) = &H53FF2073: z_Sc(74)1ennarcMemt thBm& z
'Me = z_ScMe c.ethunk ada) = llIDMe ca thBm& z
'Me = z_ScMe c.ethunk ada) = llIFess Adata
'Me = z_ScMe c.ethunk ada) = llz
' a28Uonst NDATAo
'Me = z_ScMemt thBm& z
'Me emt thBm& z
E) = aers are dependent> Wn)n,ChEn'nst NDATAo
vTy &H53FF2073: z_Sarget object
'Cacivate Sub INDX_EBXEbackthe ======e53FF2073: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz3FF2073: b INDX_Eallback. If undefined, callbackth subclasseMSDN, the c 'Cacivate Sub IND llIFess A: z_Sarggggh is MMMMMMMMMe mem ordina) = llIDMe ca thBm& z
'Me =et oCallback = Me 'IIIIIIIIIIIr)aENES 'III65i =et oCacalled directly
zUnThunk nOrdinal, Cal Else
ealled callFet theOphhhhU CuuuuuuuuuuuuuuuuuuuMh N, t nOrdinal, Cal Else
eeive th9u Else
'M scb_Setf table entry count
D llIFess A: z_Sarggggh ehhhhU CuuuuuuuuuuuuuuuuuuuMh N, t nOrdinal, CrUnThuac nOrdi(6) =o1)